package com.wss.module.main.ui.view.ca;

import com.orhanobut.logger.Logger;
import com.orhanobut.logger.ResourceTable;
import ohos.agp.components.AttrSet;
import ohos.agp.components.ComponentContainer;
import ohos.agp.components.Image;
import ohos.agp.components.StackLayout;
import ohos.agp.components.surfaceprovider.SurfaceProvider;
import ohos.agp.graphics.SurfaceOps;
import ohos.agp.utils.Rect;
import ohos.agp.window.service.DisplayManager;
import ohos.app.Context;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.global.configuration.Configuration;
import ohos.media.image.common.Size;

import java.io.IOException;

public class SenseCameraPreview extends StackLayout implements
        ComponentContainer.ArrangeListener {

    private Context mContext;

    private SurfaceProvider mSurfaceView;

    private boolean mStartRequested;

    private boolean mSurfaceAvailable;

    private SenseCamera mCamera;

    private StartListener mStartListener;

    public SenseCameraPreview(Context context) {
        this(context, null);
    }

    public SenseCameraPreview(Context context, AttrSet attrSet) {
        this(context, attrSet, null);
    }

    public SenseCameraPreview(Context context, AttrSet attrSet, String styleName) {
        super(context, attrSet, styleName);

        this.mContext = context;
        this.mStartRequested = false;
        this.mSurfaceAvailable = false;

        this.mSurfaceView = new SurfaceProvider(getContext());
        this.mSurfaceView.pinToZTop(false);
        LayoutConfig config = new LayoutConfig(LayoutConfig.MATCH_PARENT, LayoutConfig.MATCH_PARENT);
        this.mSurfaceView.setLayoutConfig(config);
        this.mSurfaceView
                .getSurfaceOps()
                .get()
                .addCallback(new SurfaceCallBack());
        addComponent(mSurfaceView);

        System.out.println("==== camera preview init .....");
    }

    public void setStartListener(final StartListener listener) {
        this.mStartListener = listener;
    }

    /**
     * start with SenseCamera.
     *
     * @param senseCamera senseCamera.
     */
    public void start(final SenseCamera senseCamera) throws IOException, RuntimeException {

        if (senseCamera == null) {
            this.stop();
        }

        this.mCamera = senseCamera;

        if (this.mCamera != null) {
            this.mStartRequested = true;
            this.startIfReady();
        }
    }

    /**
     * stop.
     */
    public void stop() {
        if (this.mCamera != null) {
            this.mCamera.stop();
        }
    }

    /**
     * release.
     */
    public void release() {
        if (this.mCamera != null) {
            this.mCamera.release();
            this.mCamera = null;
        }
    }

    private void startIfReady() throws IOException, RuntimeException {

        System.out.println("==== start requested ... " + mStartRequested );
        System.out.println("==== surface available ... " + mSurfaceAvailable );

        if (this.mStartRequested && this.mSurfaceAvailable) {
            this.mCamera.start(this.mSurfaceView);
            this.postLayout();
            this.mStartRequested = false;
        }
    }

    @Override
    public boolean onArrange(final int left, final int top, final int right,
                             final int bottom) {
        if (this.mCamera != null) {

            Size size = this.mCamera.getPreviewSize();

            if (size != null) {
                int width = size.width;
                int height = size.height;

                if (this.isPortraitMode()) {
                    int tmp = width;
                    //noinspection SuspiciousNameCombination
                    width = height;
                    height = tmp;
                }

                final int layoutWidth = right - left;
                final int layoutHeight = bottom - top;

                int childWidth;
                int childHeight;

                final float layoutAspectRatio = layoutWidth / (float) layoutHeight;
                final float cameraPreviewAspectRatio = width / (float) height;

                if (Float.compare(layoutAspectRatio, cameraPreviewAspectRatio) <= 0) {
                    childWidth = (int) (layoutHeight * cameraPreviewAspectRatio);
                    childHeight = layoutHeight;
                } else {
                    childWidth = layoutWidth;
                    childHeight = (int) (layoutWidth / cameraPreviewAspectRatio);
                }

                for (int i = 0; i < this.getChildCount(); ++i) {
                    LayoutConfig layoutConfig = new LayoutConfig();
                    this.getComponentAt(i)
                            .setComponentPosition(0, 0, childWidth, childHeight);
                }

                try {
                    this.startIfReady();
                } catch (Exception e) {
                    if (SenseCameraPreview.this.mStartListener != null) {
                        SenseCameraPreview.this.mStartListener.onFail();
                    }
                }
            }

        }
        return true;
    }

    /**
     * convert viewRect.
     *
     * @param viewRect viewRect.
     * @return converted Rect.
     */
    @SuppressWarnings("SuspiciousNameCombination")
    public Rect convertViewRectToCameraPreview(final Rect viewRect) {
        final int viewWidth = this.getWidth();
        final int viewHeight = this.getHeight();
        final int cameraRotationDegrees = this.mCamera.getRotationDegrees();
        final int imageWidth = this.mCamera.getPreviewSize().width;
        final int imageHeight = this.mCamera.getPreviewSize().height;

        float widthRatio;
        float heightRatio;

        switch (cameraRotationDegrees) {
            case 90:
            case 270:
                widthRatio = (float) imageHeight / viewWidth;
                heightRatio = (float) imageWidth / viewHeight;
                break;
            case 0:
            case 180:
            default:
                widthRatio = (float) imageWidth / viewWidth;
                heightRatio = (float) imageHeight / viewHeight;
                break;
        }

        final float scale = widthRatio < heightRatio ? widthRatio : heightRatio;

        final Rect scaledRect =
                new Rect((int) (viewRect.left * scale + 0.5f), (int) (viewRect.top * scale + 0.5f),
                        (int) (viewRect.right * scale + 0.5f),
                        (int) (viewRect.bottom * scale + 0.5f));

        switch (cameraRotationDegrees) {
            case 90:
                return new Rect(scaledRect.top, imageHeight - scaledRect.right, scaledRect.bottom,
                        imageHeight - scaledRect.left);
            case 180:
                return new Rect(imageWidth - scaledRect.right, imageHeight - scaledRect.bottom,
                        imageWidth - scaledRect.left, imageHeight - scaledRect.top);
            case 270:
                return new Rect(imageWidth - scaledRect.bottom, scaledRect.left,
                        imageWidth - scaledRect.top, scaledRect.right);
            case 0:
            default:
                return scaledRect;
        }
    }

    /**
     * convert viewRect.
     *
     * @param viewRect viewRect.
     * @return converted Rect.
     */
    @SuppressWarnings("SuspiciousNameCombination")
    public Rect convertViewRectToPicture(final Rect viewRect) {
        final int viewWidth = this.getWidth();
        final int viewHeight = this.getHeight();
        final int cameraRotationDegrees = this.mCamera.getRotationDegrees();
        final int imageWidth = this.mCamera.getPreviewSize().width;
        final int imageHeight = this.mCamera.getPreviewSize().height;

        float widthRatio;
        float heightRatio;

        switch (cameraRotationDegrees) {
            case 90:
            case 270:
                widthRatio = (float) imageHeight / viewWidth;
                heightRatio = (float) imageWidth / viewHeight;
                break;
            case 0:
            case 180:
            default:
                widthRatio = (float) imageWidth / viewWidth;
                heightRatio = (float) imageHeight / viewHeight;
                break;
        }

        final float scale = widthRatio < heightRatio ? widthRatio : heightRatio;

        final Rect scaledRect =
                new Rect((int) (viewRect.left * scale + 0.5f), (int) (viewRect.top * scale + 0.5f),
                        (int) (viewRect.right * scale + 0.5f),
                        (int) (viewRect.bottom * scale + 0.5f));

        Rect rotateRect = new Rect(scaledRect);
        switch (cameraRotationDegrees) {
            case 90:
                rotateRect =
                        new Rect(scaledRect.top, imageHeight - scaledRect.right, scaledRect.bottom,
                                imageHeight - scaledRect.left);
                break;
            case 180:
                rotateRect =
                        new Rect(imageWidth - scaledRect.right, imageHeight - scaledRect.bottom,
                                imageWidth - scaledRect.left, imageHeight - scaledRect.top);
                break;
            case 270:
                rotateRect = new Rect(imageWidth - scaledRect.bottom, scaledRect.left,
                        imageWidth - scaledRect.top, scaledRect.right);
                break;
            case 0:
            default:
                break;
        }

        Rect resultRect = new Rect(rotateRect);
        if (this.mCamera.getCameraFacing() == SenseCamera.CAMERA_FACING_FRONT) {
            switch (cameraRotationDegrees) {
                case 90:
                case 270:
                    resultRect = new Rect(rotateRect.left, imageHeight - rotateRect.bottom,
                            rotateRect.right, imageHeight - rotateRect.top);
                    break;
                case 0:
                case 180:
                    resultRect = new Rect(imageWidth - rotateRect.right, rotateRect.top,
                            imageWidth - rotateRect.left, rotateRect.bottom);
                    break;
                default:
                    break;
            }
        }

        return resultRect;
    }

    private boolean isPortraitMode() {
        final int orientation = DisplayManager.getInstance()
                .getDefaultDisplay(getContext()).get()
                .getRotation();
        return orientation != Configuration.DIRECTION_HORIZONTAL
                && orientation == Configuration.DIRECTION_VERTICAL;
    }

    public interface StartListener {
        void onFail();
    }

    private class SurfaceCallBack implements SurfaceOps.Callback {
        @Override
        public void surfaceCreated(SurfaceOps callbackSurfaceOps) {
            System.out.println("==== surface callback....");
            SenseCameraPreview.this.mSurfaceAvailable = true;
            callbackSurfaceOps.setFixedSize(1920, 1080);
            new EventHandler(EventRunner.getMainEventRunner())
                    .postTask(new Runnable() {
                        @Override
                        public void run() {
                            try {
                                startIfReady();
                            } catch (IOException e) {
                                e.printStackTrace();
                                if (mStartListener != null) {
                                    mStartListener.onFail();
                                }
                            }
                        }
                    }, 200);


        }

        @Override
        public void surfaceChanged(SurfaceOps callbackSurfaceOps,
                                   int format,
                                   int width,
                                   int height) {
            System.out.println("==== surface changed....");
        }

        @Override
        public void surfaceDestroyed(SurfaceOps callbackSurfaceOps) {
            SenseCameraPreview.this.mSurfaceAvailable = false;
            System.out.println("==== surface destroyed....");
        }
    }
}
